using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using AuthService.Application.Tokens;
using AuthService.Domain.Tokens;

namespace AuthService.EntityFrameworkCore.Tokens
{
    /// <summary>
    /// 令牌仓储实现
    /// </summary>
    public class TokenRepository : EfCoreRepository<AuthServiceDbContext, Token, Guid>, ITokenRepository
    {
        public TokenRepository(IDbContextProvider<AuthServiceDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }

        public async Task<Token> GetByAccessTokenAsync(string accessToken, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .FirstOrDefaultAsync(x => x.AccessToken == accessToken, GetCancellationToken(cancellationToken));
        }

        public async Task<Token> GetByRefreshTokenAsync(string refreshToken, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .FirstOrDefaultAsync(x => x.RefreshToken == refreshToken, GetCancellationToken(cancellationToken));
        }

        public async Task<List<Token>> GetActiveTokensByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.UserId == userId && x.IsActive)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<Token>> GetTokensByClientIdAsync(string clientId, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.ClientId == clientId)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<Token>> GetTokensByUserIdAndClientIdAsync(Guid userId, string clientId, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.UserId == userId && x.ClientId == clientId)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<Token>> GetExpiredTokensAsync(CancellationToken cancellationToken = default)
        {
            var now = DateTime.Now;
            return await DbSet
                .Where(x => x.ExpiresAt < now || x.RefreshExpiresAt < now)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<Token>> GetRevokedTokensAsync(CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => !x.IsActive || x.RevokedAt.HasValue)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task RevokeAllTokensByUserIdAsync(Guid userId, string reason = "", CancellationToken cancellationToken = default)
        {
            var tokens = await GetActiveTokensByUserIdAsync(userId, cancellationToken);
            foreach (var token in tokens)
            {
                token.Revoke(reason);
            }
            await SaveChangesAsync(cancellationToken);
        }

        public async Task RevokeAllTokensByClientIdAsync(string clientId, string reason = "", CancellationToken cancellationToken = default)
        {
            var tokens = await GetTokensByClientIdAsync(clientId, cancellationToken);
            foreach (var token in tokens)
            {
                token.Revoke(reason);
            }
            await SaveChangesAsync(cancellationToken);
        }

        public async Task RevokeRefreshTokensByUserIdAsync(Guid userId, string reason = "", CancellationToken cancellationToken = default)
        {
            var tokens = await GetActiveTokensByUserIdAsync(userId, cancellationToken);
            foreach (var token in tokens)
            {
                token.RefreshToken = "revoked";
                token.Revoke(reason);
            }
            await SaveChangesAsync(cancellationToken);
        }

        public async Task<int> CleanupExpiredTokensAsync(CancellationToken cancellationToken = default)
        {
            var expiredTokens = await GetExpiredTokensAsync(cancellationToken);
            if (expiredTokens.Any())
            {
                await DeleteManyAsync(expiredTokens, autoSave: true, cancellationToken: cancellationToken);
            }
            return expiredTokens.Count;
        }

        public async Task<TokenStatisticsDto> GetTokenStatisticsAsync(Guid? userId = null, string clientId = null, CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();
            if (userId.HasValue)
            {
                query = query.Where(x => x.UserId == userId.Value);
            }
            if (!string.IsNullOrEmpty(clientId))
            {
                query = query.Where(x => x.ClientId == clientId);
            }

            var tokens = await query.ToListAsync(cancellationToken);

            return new TokenStatisticsDto
            {
                TotalTokens = tokens.Count,
                ActiveTokens = tokens.Count(x => x.IsActive && x.ExpiresAt > DateTime.Now),
                ExpiredTokens = tokens.Count(x => x.ExpiresAt <= DateTime.Now),
                RevokedTokens = tokens.Count(x => !x.IsActive || x.RevokedAt.HasValue),
                AccessTokens = tokens.Count(x => !string.IsNullOrEmpty(x.AccessToken)),
                RefreshTokens = tokens.Count(x => !string.IsNullOrEmpty(x.RefreshToken)),
                TokensByType = tokens.GroupBy(x => x.TokenType ?? "Unknown").ToDictionary(g => g.Key, g => g.Count()),
                TokensByClient = tokens.GroupBy(x => x.ClientId ?? "Unknown").ToDictionary(g => g.Key, g => g.Count())
            };
        }
    }
}